package org.rk.core.filesys.util;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.rk.core.common.exception.FileNameLengthLimitExceededException;
import org.rk.core.common.exception.InvalidExtensionException;
import org.rk.core.common.util.RKAlert;
import org.rk.core.common.util.RkStrUtil;
import org.rk.core.common.util.encrypt.MD5Util;
import org.rk.core.filesys.bean.FileConditionInfo;
import org.rk.core.filesys.bean.FileInfoBean;
import org.springframework.web.multipart.MultipartFile;

public class FileUploadUtils {
	
	//private static GridFsClient gridFsClient=AppCtxUtil.getBean("gridFsClient");
	
	public static final long DEFAULT_MAX_SIZE = 52428800 * 2 * 10 * 100;
	public static String key="cavion@caorendao187@163.com";
	
	/**
	 * 
	 * @author：曹仁道
	 * @描述：文件上传
	 * @param file 上传的文件
	 * @param appKey 来源系统appkey
	 * @param opraType 系统对应的子业务
	 * @return
	 * 2016年9月19日 下午5:35:42
	 */
	public static final FileInfoBean upload(MultipartFile file,FileConditionInfo fileConditionInfo,String userName) {
		
		try {
			return uploadCheck(file,fileConditionInfo,userName);
		} catch (InvalidExtensionException.InvalidImageExtensionException e) {
			RKAlert.Error("upload.not.allow.image.extension");
		} catch (InvalidExtensionException.InvalidFlashExtensionException e) {
			RKAlert.Error("upload.not.allow.flash.extension");
		} catch (InvalidExtensionException.InvalidMediaExtensionException e) {
			RKAlert.Error("upload.not.allow.media.extension");
		} catch (InvalidExtensionException e) {
			RKAlert.Error("upload.not.allow.extension");
		} catch (FileUploadBase.FileSizeLimitExceededException e) {
			RKAlert.Error("upload.exceed.maxSize");
		} catch (FileNameLengthLimitExceededException e) {
			RKAlert.Error("upload.filename.exceed.length");
		} catch (IOException e) {
			RKAlert.Error("上传发生未知错误");
			e.printStackTrace();
		}
		return null;
	}
	
	private static final String genBaseDir(FileConditionInfo fileConditionInfo){
		String baseDir=fileConditionInfo.getModuleCode()+File.separator+fileConditionInfo.getOpareType();
		return baseDir;
	}
	/**
	 * @方法描述：上传前置验证
	 * @author:曹仁道
	 * 2016年11月22日 下午6:21:19
	 * @param file
	 * @param bizMode
	 * @param userName
	 * @return
	 * @throws FileNameLengthLimitExceededException 
	 * @throws InvalidExtensionException 
	 * @throws FileSizeLimitExceededException 
	 * @throws IOException 
	 */
	private static final FileInfoBean uploadCheck(MultipartFile file,FileConditionInfo fileConditionInfo,String userName) throws FileNameLengthLimitExceededException, FileSizeLimitExceededException, InvalidExtensionException, IOException {
		FileInfoBean fileInfo=new FileInfoBean();
		fileInfo.setCreator(userName);
		fileInfo.setSourceName(file.getOriginalFilename());
		fileInfo.setMime(file.getContentType());
		fileInfo.setSize(file.getSize());
		
		String suffix=FilenameUtils.getExtension(file.getOriginalFilename());
		fileInfo.setSuffix(suffix);
		
		int fileNamelength = file.getOriginalFilename().length();
		int fileNameMaxLength=fileConditionInfo.getFileNameMaxLength();
		if (fileNamelength > fileNameMaxLength) {
			throw new FileNameLengthLimitExceededException(file.getOriginalFilename(), fileNamelength,fileNameMaxLength);
		}
		//对文件的可允许上传校验并返回文件简单类型
		String fileSimpleType=assertAllowed(file,fileConditionInfo);
		fileInfo.setSimpleType(fileSimpleType);
		
		//读取系统配置采用文件保存还是mongoDB保存
		String fileSaveMethod=fileConditionInfo.getFileSaveMethod();
		fileInfo.setSaveType(fileSaveMethod);
		if(fileSaveMethod.equals("mongodb")){
			//fileInfo=uploadToMongoDB(file,fileConditionInfo,fileInfo);
		}else{
			fileInfo=uploadToAbsolute(file,fileConditionInfo,fileInfo,true);
		}
		
		return fileInfo;
	}
	
	
	/**
	 * 文件上传
	 *
	 * @param rootPath
	 *          根目录
	 * @param baseDir
	 *          相对应用的基目录
	 * @param file
	 *          上传的文件
	 * @param needDatePathAndRandomName
	 *          是否需要日期目录和随机文件名前缀
	 * @return 返回成功上传后的文件相关信息，map
	 * @throws InvalidExtensionException
	 *           如果MIME类型不允许
	 * @throws FileSizeLimitExceededException
	 *           如果超出最大大小
	 * @throws FileNameLengthLimitExceededException
	 *           文件名太长
	 * @throws IOException
	 *           比如读写文件出错时
	 */
	private static final FileInfoBean uploadToAbsolute(MultipartFile file,FileConditionInfo fileConditionInfo,FileInfoBean fileInfo, boolean needDatePathAndRandomName) throws IOException{
		
		String baseDir=genBaseDir(fileConditionInfo);
		String rootPath=fileConditionInfo.getRootPath();
		//对文件的可允许上传校验之后进行md5加密
		String fileMd5=fileToMd5(file);
		fileInfo.setSign(fileMd5);
		
		String filename = extractFilename(file, baseDir,fileInfo.getSimpleType(), needDatePathAndRandomName);
		filename=filename+"."+fileInfo.getSuffix();
		
		fileInfo.setNewName(filename.substring(filename.lastIndexOf("\\")+1, filename.length()));
		fileInfo.setUrlPath(fileConditionInfo.getRootUrl()+File.separator+filename);
		File desc = getAbsoluteFile(rootPath, filename);
		
		fileInfo.setFileAbsolute(rootPath+File.separator+filename);
		file.transferTo(desc);
		return fileInfo;
	}
	
/*	private static final FileInfoBean uploadToMongoDB(MultipartFile file,FileConditionInfo fileConditionInfo,FileInfoBean fileInfo){
		//GridFSFile gridFSFile=null;
		try {
			//gridFSFile=gridFsClient.store(file.getInputStream(), bizMode, GridFsClient.mapToDBObject(RKBeanUtil.convertBeanToMap(fileInfo)));
			//fileInfo.setSign(gridFSFile.getMD5());
			//fileInfo.setNewFileName(gridFSFile.getFilename());
			//fileInfo.setMongoid((String) gridFSFile.getId());
			//fileInfo.setFileSize(String.valueOf(gridFSFile.getChunkSize()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return fileInfo;
	}*/
	
	private static final File getAbsoluteFile(String uploadDir, String filename) throws IOException {
		
		uploadDir = FilenameUtils.normalizeNoEndSeparator(uploadDir);
		
		File desc = new File(uploadDir + File.separator + filename);
		
		if (!desc.getParentFile().exists()) {
			desc.getParentFile().mkdirs();
		}
		if (!desc.exists()) {
			desc.createNewFile();
		}
		return desc;
	}
	
	private static final String extractFilename(MultipartFile file, String baseDir,String fileSimpleType, boolean needDatePathAndRandomName)
			throws UnsupportedEncodingException {
		String filename = file.getOriginalFilename();
		int slashIndex = filename.indexOf("/");
		if (slashIndex >= 0) {
			filename = filename.substring(slashIndex + 1);
		}
		baseDir=baseDir + File.separator + fileSimpleType + File.separator;
		if (needDatePathAndRandomName) {
			filename = baseDir + datePath("yyyyMMdd") + File.separator + encodingFilename(filename);
		} else {
			filename = baseDir + filename;
		}
		
		return filename;
	}
	
	/**
	 * 
	 * @author：曹仁道
	 * @描述：根据文件名当前系统时间，和文件记录随增数组合加密成新的文件名
	 * @param filename
	 * @return
	 * 2016年9月23日 下午4:17:15
	 */
	private static final String encodingFilename(String filename) {
		filename = filename.replace("_", " ");
		//filename = MD5Util.encrypt(filename + System.nanoTime() + counter++) + "_" + filename;
		filename = MD5Util.encrypt(filename + System.nanoTime() + key);
		return filename;
	}
	
	/**
	 * 
	 * @author：曹仁道
	 * @描述：日期路径
	 * @param format,路徑格式，可为空，默认yyyymm
	 * @return 2016年9月19日 上午11:46:07
	 */
	private static final String datePath(String format) {
		Date now = new Date();
		if (RkStrUtil.isEmpty(format)) {
			format = "yyyyMMdd";
		}
		return DateFormatUtils.format(now, format);
	}
	
	/**
	 * 是否允许文件上传
	 *
	 * @param file
	 *          上传的文件
	 * @param allowedExtension
	 *          文件类型 null 表示允许所有
	 * @param maxSize
	 *          最大大小 字节为单位 -1表示不限制
	 * @return
	 * @throws InvalidExtensionException
	 *           如果MIME类型不允许
	 * @throws FileSizeLimitExceededException
	 *           如果超出最大大小
	 */
	private static final String assertAllowed(MultipartFile file,FileConditionInfo fileConditionInfo)	throws InvalidExtensionException, FileSizeLimitExceededException {
		List<Map<String,String>> fileTypeAndExt=fileConditionInfo.getFileTypeAndExt();//ZSysParamUtil.getSysParamByCodePrefix("file_type_extension_");
		String fileType=file.getContentType();
		String filename = file.getOriginalFilename();
		String extension = FilenameUtils.getExtension(filename);
		extension=extension.toLowerCase();
		
		String fTypeAndFExt=extension+":"+fileType;
		//验证和匹配文件类型，并返回文件匹配码
		String fTypeAndFExtCode=matchAndVerifiFile(fTypeAndFExt, fileTypeAndExt,filename);
		if(RkStrUtil.isEmpty(fTypeAndFExtCode)){
			RKAlert.Error("指定文件类型不是被允许上传的的类型！");
		}
		
		long size = file.getSize();
		long maxFileSize=fileConditionInfo.getUploadMaxFileSize();
		if (maxFileSize != -1 && size > maxFileSize) {
			throw new FileSizeLimitExceededException("超出可允许上传的文件大小！", size, maxFileSize);
		}
		String[] codes=fTypeAndFExtCode.split("_");
		return codes[codes.length-1];
	}
	
	/**
	 * 
	 * @author：曹仁道
	 * @描述：匹配文件简单类型并校验文件
	 * @param typeAndExt
	 * @param fileTypeAndExtList
	 * @param filename
	 * @return
	 * @throws InvalidExtensionException
	 * 2016年9月19日 下午4:53:34
	 */
	public static final String matchAndVerifiFile(String typeAndExt, List<Map<String,String>> fileTypeAndExtList,String filename) throws InvalidExtensionException {
		String result="";
		String fileTypeAndExt="";
		for (Map<String,String> fileTypeAndExtMap : fileTypeAndExtList) {
			for (String mapKey : fileTypeAndExtMap.keySet()) {
				String[] ftAndFe=fileTypeAndExtMap.get(mapKey).split(";");
				boolean isThis=false;
				for (String str : ftAndFe) {
					String fileType=str.split(":")[0];
					String fileTypeNow=typeAndExt.split(":")[0];
					if(fileType.equalsIgnoreCase(fileTypeNow)){
						isThis=true;
					}
					if (str.equalsIgnoreCase(typeAndExt)) {
						result=mapKey;
					}
				}
				if(isThis){
					fileTypeAndExt=fileTypeAndExtMap.get(mapKey);
				}
			}
		}
		if(RkStrUtil.isEmpty(result)){
			if(!RkStrUtil.isEmpty(fileTypeAndExt)){
				String[] ftAndFe=fileTypeAndExt.split(",");
				String[] fileExt=new String[ftAndFe.length];
				for (int i = 0; i < ftAndFe.length; i++) {
					fileExt[i]=ftAndFe[i].split(":")[1];
				}
				throw new InvalidExtensionException(fileExt, typeAndExt.split(":")[1], filename);
			}else{
				RKAlert.Error("未知不允许的上传文件");
			}
		}
		return result;
	}
	
	public static final void delete(String filePath,String mongoId){
		if (!RkStrUtil.isEmpty(filePath)) {
			File desc = new File(filePath);
			if (desc.exists()) {
				desc.delete();
			}
		}else if(!RkStrUtil.isEmpty(mongoId)){
			//gridFsClient.deleteById(mongoId);
		}else{
			RKAlert.Error("该文件不存在或已被删除");
		}
	}
	
	private static final String fileToMd5(MultipartFile file){
		long fileMd5Start=System.currentTimeMillis();//计时开始
		String fileMd5="";
		try {
			byte[] fileByte=file.getBytes();
			MessageDigest md5=MessageDigest.getInstance("MD5");
			byte[] digest = md5.digest(fileByte);
	    String hashString = new BigInteger(1, digest).toString(16);
	    fileMd5=hashString.toString();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			RKAlert.Error("文件转换异常");
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			RKAlert.Error("文件加密异常");
			e.printStackTrace();
		}
		long fileMd5End=System.currentTimeMillis();//计时结束
		System.out.println("对文件MD5加密耗时："+(fileMd5End-fileMd5Start));
		return fileMd5;
	}
}
